/**
 * Title:       APScriptHandler.java
 * Copyright:   Copyright (c) 1996-2004 The Agent Factory Working Group. All rights reserved.
 * Licence:     This file is free software; you can redistribute it and/or modify
 *              it under the terms of the GNU Lesser General Public License as published by
 *              the Free Software Foundation; either version 2.1, or (at your option)
 *              any later version.
 *
 *              This file is distributed in the hope that it will be useful,
 *              but WITHOUT ANY WARRANTY; without even the implied warranty of
 *              MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *              GNU Lesser General Public License for more details.
 *
 *              You should have received a copy of the GNU Lesser General Public License
 *              along with Agent Factory; see the file COPYING.  If not, write to
 *              the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 *              Boston, MA 02111-1307, USA.
 */
package com.agentfactory.platform;

import com.agentfactory.platform.core.Agent;
import com.agentfactory.service.ams.AgentManagementService;
import com.agentfactory.platform.service.PlatformServiceDescriptor;
import com.agentfactory.platform.service.PlatformServiceManager;
import com.agentfactory.platform.util.Logger;
import com.agentfactory.platform.util.Scanner;
import java.util.StringTokenizer;
import java.util.HashMap;
import java.io.*;
import java.util.List;

/**
 * The APScriptHandler class is responsible for taking input directly from an
 * interactive console, processing the commands, and sending the result back to
 * the console.
 *
 * Platform Scripts are used to automatically run commands on the AgentFactory
 * Platform. This class provides an interperter for these scripts.
 *
 * Instructions from the command line can cause commands to be cached, these
 * commands once cached, will be completely processed before another command can
 * be taken directly from the input stream.
 *
 * @author Rem Collier
 * @author Robert Ross
 */
public class APScriptHandler {
    private static final String AMS_NAME = "fipa.std.service.ams";
    private static final String AMS_DESCRIPTION = "fipa.std.service.ams com.agentfactory.service.ams.AgentManagementService";
    private static final String SYSTEM_SERVICE = "af.sys.sup com.agentfactory.plugins.services.system.SystemService";
    private static final int CLASS_LOG_LEVEL = Logger.LEVEL_ERROR;
    
    private Scanner scanner;
    private HashMap agents;
    private AgentPlatform platform;
    private boolean systemAgents;
    private AgentManagementService ams;
    private PlatformServiceManager manager;
    
    /**
     * Construct a APScriptHandler
     *
     */
    public APScriptHandler(Scanner scanner) {
        platform = InitialisationManager.getInstance().getPlatform();
        this.scanner = scanner;
        agents = new HashMap();
        
        /**
         * Launch the AMS (if not already launched
         */
        manager = platform.getPlatformServiceManager();
        ams = (AgentManagementService) manager.getService(AMS_NAME, platform);
        if (ams == null) {
            Logger.warning("The AMS Service was not installed...", CLASS_LOG_LEVEL);
            manager.addService( new PlatformServiceDescriptor( new StringTokenizer( AMS_DESCRIPTION ) ) );
            ams = (AgentManagementService) manager.getService(AMS_NAME, platform);
            Logger.warning("The AMS Service has now been installed.", CLASS_LOG_LEVEL);
        }
        
    }
        
    public APScriptHandler(InputStream in) {
        this(new Scanner(in));
    }
    
    public boolean processScript() {
        Logger.detail("[APSCriptHandler] Starting to process script", CLASS_LOG_LEVEL);

        List list = null;
        String command = null;
        
        while (scanner.hasNext()) {
            try {
                list = scanner.nextStatement();
                if (list.size() > 0) {
                    Logger.detail("[APSCriptHandler] Processing: " + list, CLASS_LOG_LEVEL);
                    command = (String) list.get(0);
                    if (command.equals("SYSTEMAGENT"))
                        createSystemAgent(list);
                    else if (command.equals("CREATE_AGENT"))
                        createApplicationAgent(list);
                    else if (command.equals("ADD_BELIEF"))
                        Logger.error("ADD_BELIEF is no longer supported - please " +
                                "change to INITIALISE", CLASS_LOG_LEVEL);
                    else if (command.equals("INITIALISE"))
                        initialiseAgent(list);
                    else if (command.equals("START_AGENT"))
                        startAgent(list);
                }
            } catch (IOException ioe) {
                Logger.error("[APScriptHandler] Failure in processing of APS File.", CLASS_LOG_LEVEL);
                return false;
            }
        }
        
        scanner.close();
        
        return true;
    }
        
    /**
     * Used by the processScript() method to process individual lines of the
     * script.
     * @param line a line in the script.
     * @return boolean - false if the platform is to be shutdown, true otherwise
     */
    private void createSystemAgent(List list) {
        String name = (String) list.get(1);
        String type = (String) list.get(2);
        String fullname = name;

        if ((list.size() > 3) && (list.get(3).equals("platformSpecific"))) {
            fullname = name + "@" + platform.getName();
        }

        Agent agent = ams.createAgent(fullname, type);

        agents.put(name, agent);
        platform.addSystemAgent(agent);

        if (!systemAgents) {
            manager.addService(new PlatformServiceDescriptor(new StringTokenizer(SYSTEM_SERVICE, " ")));
        }

        systemAgents = true;
        
        ams.resumeAgent(fullname);
    }
    
    private void createApplicationAgent(List list) {
        String name = (String) list.get(1);
        String type = (String) list.get(2);
        String fullname = name;

        if ((list.size() > 3) && (list.get(3).equals("platformSpecific"))) {
            fullname = name + "@" + platform.getName();
        }
            
        Agent agent = ams.createAgent( fullname, type );
        if ( agent != null ) {
            agents.put( name, agent );
        } else {
            Logger.warning("Could not create agent with name: " + fullname + " and type: " + type, CLASS_LOG_LEVEL);
        }
    }
    
    private void initialiseAgent(List list) {
        String name = (String) list.get(1);
        Agent agent = (Agent) agents.get(name);
                
        if (agent == null) {
            Logger.warning("APScriptHandler:procelssLine() - no such agent: " +
                    name + " for: " + context(list, 2),CLASS_LOG_LEVEL);
        } else {
            agent.initialise(context(list, 2));
        }
    }

    public void startAgent(List list) {
        String name = (String) list.get(1);

        Agent agent = (Agent) agents.get(name);

        if (agent == null) {
            Logger.warning("APScriptHandler:processLine() - no such agent: " +
                    name + " cannot start!", CLASS_LOG_LEVEL);
        } else if (systemAgents) {
            platform.addApplicationAgent(agent.getName());
        } else {
            ams.resumeAgent(agent.getName());
        }
    }
    
    /*********************************************************************
     * PRIVATE METHODS
     *********************************************************************/
    private String context(List list, int index) {
        StringBuffer buf = new StringBuffer();
        boolean first = true;
        while (index < list.size()) {
            if (first) first = false;
            else buf.append(" ");
            
            buf.append(list.get(index++));
        }
        
        return buf.toString();
    }
}

